﻿//#define ENABLE_PROFILER
using UnityEngine;
using System.Collections.Generic;
using TTGame;
using TTGameEngine;
using System.Reflection;
using System;
using CinemaDirector;
using System.Collections;

/*
 * ExtensionsTools
 * 各种扩展
 *
 *
 * 依赖：
 *
 * ！不写重复代码！
 * @author TTun
 * @date 7/13/2017 2:58:24 PM
 */
public static class ReflectExt {

    //unuse
    public static FieldInfo GetFieldInChildren(this Type type, Type targetType, string name, bool isFindChildren, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) {
        FieldInfo fInfoResult = type.GetField(name, bindings);
        if(fInfoResult == null) {
            FieldInfo[] tmpFInfos = type.GetFields(bindings);
            Stack<FieldInfo> fInfoStack = new Stack<FieldInfo>(tmpFInfos);
            bool isFound = false;
            do {
                FieldInfo tmpFInfo = fInfoStack.Pop();
                if(tmpFInfo.FieldType.Equals(targetType)) {
                    isFound = true;
                    fInfoResult = tmpFInfo;
                } else {
                    FieldInfo[] tmpChildFInfo = tmpFInfo.FieldType.GetFields();
                    for(int i = 0; i < tmpChildFInfo.Length; i++) {
                        fInfoStack.Push(tmpChildFInfo[i]);
                    }
                }
            } while(fInfoStack.Count > 0 && isFound == false);
        }

        return fInfoResult;
    }
    //unuse
    public static object GetValueInChildren<T>(this object obj, string name, bool isFindChildren, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) {
        FieldInfo fInfo = obj.GetType().GetFieldInChildren(typeof(T), name, true);
        if(fInfo != null) {
            return fInfo.GetValue(obj);
        }
        return null;
    }

    /// <summary>
    /// 是否是 xxx[] 数组类型，不包括List。
    ///
    /// 数组和List反射获取Value不一样：（大概是这样的）
    /// []数组类型 (Array).GetType().GetValue(objValue)[0]
    /// 而List类型是(List).GetType().GetField("Array").GetValue(objValue)[0]
    /// </summary>
    /// <returns></returns>
    public static bool IsArrayType(this Type t) {
        return t.FullName.EndsWith("[]");
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="t">类型</param>
    ///  <param name="obj">类型对应的对象</param>
    /// <param name="propertyPath"></param>
    /// <param name="bindings"></param>
    /// <returns></returns>
    public static object GetValueWithPropPath(this Type t, object obj, string propertyPath, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) {
        FieldInfo fInfo = null;
        //Debug.LogFormat("---------------------------\npropertyPath = {0}", propertyPath);
        string[] paths = propertyPath.Split(new char[] { '.' });

        Type tmpType = t;
        object tmpTypeObj = obj;
        for(int i = 0; i < paths.Length; i++) {
            string path = paths[i];
            //propertyPath是“Array.data[0]”。
            //此时：
            //Array ，tmpTypeObj已经是数组对象。
            //List  ，tmpTypeObj是List对象。tmpType是List类型。
            //其他类型比如Queue,Dictionary不会被序列化为SerializedProperty，所以不用考虑
            if(path == "Array") {
                string elemIdx = paths[i + 1];
                int dataIdx = elemIdx.Substring(5, elemIdx.Length - 5 - 1).intValue();
                if(tmpType.IsArray) {
                    //  Array 获取数组元素类型和数组索引的元素对象
                    tmpType = tmpType.GetElementType();
                    tmpTypeObj = (tmpTypeObj as Array).GetValue(dataIdx);
                } else {
                    // List 获取数组元素类型和数组索引的元素对象
                    tmpType = tmpType.GetProperty("Item").PropertyType;
                    var list = (tmpTypeObj as IEnumerable);
                    int idx = 0;
                    foreach(var item in list) {
                        if(idx == dataIdx) {
                            tmpTypeObj = item;
                            break;
                        }
                        idx++;
                    }
                }
                i++;
            } else {
                fInfo = tmpType.GetField(path, bindings);
                if(fInfo != null) {
                    tmpTypeObj = fInfo.GetValue(tmpTypeObj);
                    tmpType = fInfo.FieldType;
                }
            }
            if(fInfo == null) {
                break;
            }
        }

        return tmpTypeObj;
    }

    public static object New(this Type type) {
        return Activator.CreateInstance(type);
    }

    public static object New(this Type type, params object[] args) {
        if(args == null || args.Length == 0) {
            return Activator.CreateInstance(type);
        }

        return Activator.CreateInstance(type, args);
    }

}

public static class MathExt {
    public static Vector3 SetX(this Vector3 aVec, float aX) {
        aVec.x = aX;
        return aVec;
    }
    public static Vector3 SetY(this Vector3 aVec, float aY) {
        aVec.y = aY;
        return aVec;
    }
    public static Vector3 SetZ(this Vector3 aVec, float aZ) {
        aVec.z = aZ;
        return aVec;
    }
    public static bool distanceLess(this Vector3 p, Vector3 other, float range) {
        Vector3 v = p - other;
        v.y = 0f;
        return v.sqrMagnitude < range * range;
    }
    public static bool distanceLess(this Vector3 p, Vector3 other, Vector3 vectory) {
        return (p - other).sqrMagnitude < vectory.sqrMagnitude;
    }

    public static string RandomValue(this string[] arr) {
        if(arr == null || arr.Length == 0) {
            return "";
        }
        return arr[UnityEngine.Random.Range(0, arr.Length)];
    }

    // 两个向量是否是相同方向。使用点积公式计算。
    public static bool IsSameDirection(this Vector3 p, Vector3 other) {
        return (p.x * other.x + p.z * other.z) > 0f;
    }

    static public bool Equals(this Vector3 pos1, Vector3 pos2) {
        if(Mathf.Abs(pos1.x - pos2.x) < 0.00001f
                && Mathf.Abs(pos1.y - pos2.y) < 0.00001f
                && Mathf.Abs(pos1.z - pos2.z) < 0.00001f) {
            return true;
        }
        return false;
    }
    static public bool IsZero(this Vector3 pos1) {
        if(pos1.x == 0f
                && pos1.y == 0f
                && pos1.z == 0f) {
            return true;
        }
        return false;
    }

    static public bool IsZero2D(this Vector3 pos1) {
        if(pos1.x == 0f && pos1.z == 0f) {
            return true;
        }
        return false;
    }

    //不比较Y轴
    static public bool Equals2D(Vector3 pos1, Vector3 pos2) {
        if(Mathf.Abs(pos1.x - pos2.x) < 0.00001f
                && Mathf.Abs(pos1.z - pos2.z) < 0.00001f) {
            return true;
        }
        return false;
    }

}


public static class UnityExt {

    //新建脚本。如果已有脚本，就不新建了。
    public static T AddComponentSafe<T>(this GameObject go) where T : Component {
        if(go == null) { return null; }
        T script = go.GetComponent<T>();
        if(script == null) {
            script = go.AddComponent<T>();
        }
        return script;
    }

    //获得脚本。有非空判断，为空会报错。
    public static T GetComponentSafe<T>(this GameObject obj) where T : Component {
        T component = obj.GetComponent<T>();
        if(component == null) {
            DebugUtil.LogWarning("[GetSafeComponent] Expected to find component of type "
                                 + typeof(T) + " but found none. name:" + obj.name);
        }
        return component;
    }
    public static T GetComponentSafe<T>(this Transform obj) where T : Component {
        T component = obj.GetComponent<T>();
        if(component == null) {
            DebugUtil.LogWarning("[GetSafeComponent] Expected to find component of type "
                                 + typeof(T) + " but found none. name:" + obj.name);
        }
        return component;
    }

    public static void RemoveComponentSafe<T>(this GameObject go) where T : Component {
        if(go == null) { return; }
        UnityEngine.Object.Destroy(go.GetComponent<T>());
    }
    public static void RemoveComponentsSafe<T>(this GameObject go) where T : Component {
        if(go == null) { return; }
        T[] coms = go.GetComponents<T>();
        for(int i = 0; i < coms.Length; i++) {
            UnityEngine.Object.Destroy(coms[i]);
        }
    }

    public static void SetLayerInChildren(this GameObject obj, int layer) {
        if(null == obj) return;
        foreach(Transform trans in obj.GetComponentsInChildren<Transform>(true)) {
            trans.gameObject.layer = layer;
        }
    }

    //添加脚本。并保证没有其他基类脚本。
    public static ComT AddCompoentExcludeOtherBaseClass<ComT, excludeBaseT>(this GameObject obj)
    where ComT : excludeBaseT
    where excludeBaseT : Component {
        excludeBaseT[] coms = obj.GetComponents<excludeBaseT>();
        bool hasCom = false;
        ComT resultCom = null;
        for(int i = 0; i < coms.Length; i++) {
            if(hasCom == false && coms[i] is ComT) {
                hasCom = true;
                resultCom = coms[i] as ComT;
            } else {
                UnityEngine.Object.Destroy(coms[i]);
            }
        }
        if(hasCom == false) {
            resultCom = obj.AddComponent<ComT>();
        }
        return resultCom;
    }

    public static void memset<T>(this System.Array arr, T value) {
        int count = arr.Length;
        for(int i = 0; i < count; i++) {
            arr.SetValue(value, i);
        }
    }


    public static void SetLocalPosition(this Component _target, Vector3 v) {
        if(_target == null) return;
        _target.transform.localPosition = v;
    }

    public static void SetLocalPositionX(this Component _target, float f) {
        if(_target == null) return;
        _target.transform.localPosition = new Vector3(f, _target.transform.localPosition.y, _target.transform.localPosition.z);
    }

    public static void SetLocalPositionY(this Component _target, float f) {
        if(_target == null) return;
        _target.transform.localPosition = new Vector3(_target.transform.localPosition.x, f, _target.transform.localPosition.z);
    }

    public static void SetLocalPositionZ(this Component _target, float f) {
        if(_target == null) return;
        _target.transform.localPosition = new Vector3(_target.transform.localPosition.x, _target.transform.localPosition.y, f);
    }

    //能让tansform 直接设置active
    public static void SetActive(this Component _tran, bool active) {
        if(_tran == null)
            return;
        if(_tran.gameObject.activeSelf == active)
            return;

        _tran.gameObject.SetActive(active);
    }

    public static bool IsActive(this Component _tran) {
        if(_tran != null)
            return _tran.gameObject.activeSelf;
        return false;
    }

}

public static class GameExt {
    public static List<Puppet> objNearSortAscending(this List<Puppet> objList, Vector3 ownerPos) {
        if(objList.Count <= 1) return objList;
        objList.Sort(delegate(Puppet p1, Puppet p2) {
            float d1 = (ownerPos - p1.objTF.position).sqrMagnitude;
            float d2 = (ownerPos - p2.objTF.position).sqrMagnitude;
            if(d1 < d2) {
                return -1;
            } else if(d1 == d2) {
                return 0;
            }
            return 1;
        });
        return objList;
    }

    //从前X个对象中，随机选择一个。
    public static Puppet objSelectOneOfFirstRandom(this List<Puppet> objList, int firstMaxCount) {
        int selectCount = Mathf.Clamp(firstMaxCount, 1, objList.Count);
        int selectIdx = UnityEngine.Random.Range(0, selectCount - 1);
        return objList[selectIdx];
    }

    public static Puppet objSelectAlive(this List<Puppet> objList) {
        for(int i = 0; i < objList.Count; i++) {
            if(objList[i].IsDead == false && objList[i].Invisable) {
                return objList[i];
            }
        }
        return null;
    }

    public static void objHpSortAscending(this List<Puppet> objList) {
        objList.Sort(delegate(Puppet p1, Puppet p2) {
            float p1HPPct = p1.CurHpPrecent;
            float p2HPPct = p2.CurHpPrecent;
            if(p1HPPct < p2HPPct) {
                return -1;
            } else if(p1HPPct == p2HPPct) {
                return 0;
            }
            return 1;
        });
    }

    public static void objHpSortDescending(this List<Puppet> objList) {
        objList.Sort(delegate(Puppet p1, Puppet p2) {
            float p1HPPct = p1.CurHpPrecent;
            float p2HPPct = p2.CurHpPrecent;
            if(p1HPPct < p2HPPct) {
                return 1;
            } else if(p1HPPct == p2HPPct) {
                return 0;
            }
            return -1;
        });
    }

    //战斗位置排序。英雄的SeatPrior数字小的优先。怪物没有SeatPrior，以最大值计算。
    public static void objSeatPriorSort(this List<Puppet> objList) {
        objList.Sort(delegate(Puppet p1, Puppet p2) {
            if(p1.SeatPrior < p2.SeatPrior) {
                return -1;
            } else if(p1.SeatPrior == p2.SeatPrior) {
                return 0;
            }
            return 1;
        });
    }
    //战斗头像Icon排序。晋升（降序）  等级（降序） 英雄ID（升序）     先比较晋升 再等级  最后英雄ID
    public static void objHeadIconSort(this List<Puppet> objList) {
        objList.Sort(delegate(Puppet p1, Puppet p2) {
            if(p1.CurHp < p2.CurHp) {
                return 1;
            } else if(p1.Lv < p2.Lv) {
                return 1;
            } else if(p1.PrefabID > p2.PrefabID) {
                return 1;
            }
            return -1;
        });
    }

    //获取最近的敌人
    public static Puppet getObjNear(this List<Puppet> objList, Puppet owner) {
        float minDist = float.MaxValue;
        Puppet obj = null;
        Vector3 pos = owner.objTF.position;
        for(int i = 0; i < objList.Count; i++) {
            float tempDist = (pos - objList[i].objTF.position).sqrMagnitude;
            if(minDist > tempDist) {
                minDist = tempDist;
                obj = objList[i];
            }
        }
        return obj;
    }

    //同方向上最近的一个
    public static Puppet getObjNearWithDirection(this List<Puppet> objList, Puppet owner) {
        float minDist = float.MaxValue;
        Puppet obj = null;
        Vector3 pos = owner.objTF.position;
        for(int i = 0; i < objList.Count; i++) {
            Vector3 tempPos = objList[i].objTF.position;
            float tempDist = (pos - tempPos).sqrMagnitude;
            if(minDist > tempDist && pos.IsSameDirection(tempPos)) {
                minDist = tempDist;
                obj = objList[i];
            }
        }
        return obj;
    }

    /// <summary>
    /// 从ColliderInfo信息，得到DRCollider组件。
    /// </summary>
    /// <param name="colInfo"></param>
    /// <param name="colBindTF">碰撞框要用到的绑定对象，可以让碰撞框一直跟随该对象</param>
    /// <param name="scriptAddObj">DRCollider脚本要放置的对象</param>
    /// <param name="layerMask">碰撞的layerMask</param>
    /// <returns></returns>
    static public DamageReceiverCollider AddDRColliderComponent(ColliderInfo colInfo, Transform colBindTF, GameObject scriptAddObj, LayerMask layerMask) {
        DamageReceiverCollider drCollider = null;
        switch(colInfo.m_atkShape) {
            case ColliderShape.Sphere: {

                    DamageReceiverSphereCollider collider = scriptAddObj.AddCompoentExcludeOtherBaseClass<DamageReceiverSphereCollider, DamageReceiverCollider>();
                    collider.Init(colBindTF, colInfo.m_radius, layerMask);
                    drCollider = collider;
                }
                break;
            case ColliderShape.Rect: {
                    DamageReceiverBoxCollider collider = scriptAddObj.AddCompoentExcludeOtherBaseClass<DamageReceiverBoxCollider, DamageReceiverCollider>();
                    collider.Init(colBindTF, colInfo.m_rectSize, colInfo.m_rectCenterOffset, layerMask);
                    drCollider = collider;
                }
                break;
            case ColliderShape.Sector: {
                    DamageReceiverSectorCollider collider = scriptAddObj.AddCompoentExcludeOtherBaseClass<DamageReceiverSectorCollider, DamageReceiverCollider>();
                    collider.Init(colBindTF, colInfo.m_radius, colInfo.m_angle, layerMask);
                    drCollider = collider;
                }
                break;
            case ColliderShape.Line: {
                    DamageReceiverLineCollider collider = scriptAddObj.AddCompoentExcludeOtherBaseClass<DamageReceiverLineCollider, DamageReceiverCollider>();
                    collider.Init(colBindTF, colInfo.m_moveSpeed, colInfo.m_rayLenFactor, layerMask);
                    drCollider = collider;
                }
                break;
        }

        return drCollider;
    }

    static public SkillTL GetSkill(this TimelineItem timelineItem) {
        Transform skillObjTF = timelineItem.Cutscene.transform.parent;
        return skillObjTF.GetComponent<SkillTL>();
    }

    //Dict<ObjID, WeakRef(Com)>
    //缓存获得的组件。实验证明每次GetComponent都会产生0.6K的内存.
//     static Dictionary<int, WeakReference> comCaches = new Dictionary<int, WeakReference>();
//
//     static public void ClearComCache() {
//         comCaches.Clear();
//     }
//
//     static private T GetComponentCache<T>(int objID) {
//         WeakReference wr = null;
//         T resultCom = default(T);
//         int hashCode = typeof(T).GetHashCode();
//         if(comCaches.TryGetValue(objID + hashCode, out wr)) {
//             if(wr != null && wr.IsAlive) {
//                 resultCom = (T)wr.Target;
//             }
//         }
//
//         return resultCom;
//     }
//
//     static private void AddComponentCache<T>(int objID, T com) {
//         int hashCode = typeof(T).GetHashCode();
//         //如果重复添加了，那就错了哦！
//         comCaches.Add(objID + hashCode, new WeakReference(com));
//     }
//
//     //高效节能环保...新能源
//     //缓存组件。风险和收益不成正比。
//     public static T GetComponentFast<T>(this GameObject go) {
//         DebugUtil.LogFormat("T:{0}/{1}  go:{2}/{3}", typeof(T).Name, typeof(T).GetHashCode(), go.name, go.GetInstanceID());
//         Profiler.BeginSample("ExtTools.GetComGOFast. " + typeof(T).Name);
//         T com = GetComponentCache<T>(go.GetInstanceID());
//         if(com == null) {
//             com = go.GetComponent<T>();
//             if(com != null) {
//                 AddComponentCache<T>(go.GetInstanceID(), com);
//             }
//         }
//
//         Profiler.EndSample();
//         return com;
//     }
//     public static T GetComponentInChildrenFast<T>(this GameObject go) {
//         //Profiler.BeginSample("ExtTools.GetComInChildrenGO." + typeof(T).Name);
//         T com = go.GetComponentInChildren<T>();
//         //Profiler.EndSample();
//         return com;
//     }
//
//     //youhua:使用GetComponentsInChildren(List) 减少临时数组创建？
//     public static T[] GetComponentsInChildrenFast<T>(this GameObject go, bool includeInactive = false) {
//         //Profiler.BeginSample("ExtTools.GetComsssInChildrenGO." + typeof(T).Name);
//         T[] coms = go.GetComponentsInChildren<T>(includeInactive);
//         //Profiler.EndSample();
//         return coms;
//     }
//
//     public static T GetComponentFast<T>(this Component go) {
//         DebugUtil.LogFormat("T:{0}/{1}  go:{2}/{3}", typeof(T).Name, typeof(T).GetHashCode(), go.name, go.GetInstanceID());
//         Profiler.BeginSample("ExtTools.GetComComFast. " + typeof(T).Name);
//         T com = GetComponentCache<T>(go.GetInstanceID());
//         if(com == null) {
//             com = go.GetComponent<T>();
//             if(com != null) {
//                 AddComponentCache<T>(go.GetInstanceID(), com);
//             }
//         }
//
//         Profiler.EndSample();
//         return com;
//     }
//
//     public static T GetComponentInChildrenFast<T>(this Component go) {
//         //Profiler.BeginSample("Ext.GetComInChildrenCom." + typeof(T).Name);
//         T com = go.GetComponentInChildren<T>();
//         //Profiler.EndSample();
//         return com;
//     }
//
//     //youhua:使用GetComponentsInChildren(List) 减少临时数组创建？
//     public static T[] GetComponentsInChildrenFast<T>(this Component go, bool includeInactive = false) {
//         //Profiler.BeginSample("ExtTools.GetComsssInChildrenCom." + typeof(T).Name);
//         T[] coms = go.GetComponentsInChildren<T>(includeInactive);
//         //Profiler.EndSample();
//         return coms;
//     }
//
//     public static T AddComponentFast<T>(this GameObject go) where T : Component {
//         //Profiler.BeginSample("ExtTools.AddComCom." + typeof(T).Name);
//         T com = go.AddComponent<T>();
//         //Profiler.EndSample();
//         return com;
//     }

}


public static class DataStructExt {

    public static bool TryAdd<TChild>(this List<TChild> list, TChild value) {
        if(!list.Contains(value)) {
            list.Add(value);
            return true;
        }
        return false;
    }

    public static TChild TryGet<TChild>(this List<TChild> list, int idx, TChild defaultValue) {
        if(idx >= list.Count) {
            return defaultValue;
        } else {
            return list[idx];
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <returns>已存在，返回true</returns>
    /// <param name="dic">Dic.</param>
    /// <param name="key">Key.</param>
    /// <param name="value">Value.</param>
    /// <param name="isCover">false，已存在</param>
    public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue value, bool isCover = true) {
        if(dic.ContainsKey(key)) {
            if(isCover) dic[key] = value;
            return false;
        } else {
            dic.Add(key, value);
            return true;
        }
    }

    public static int intValue(this string str, int defaultValue = 0) {
        int ret = defaultValue;
        if(false == int.TryParse(str, out ret)) {
            DebugUtil.LogErrorFormat("[intValue]Error, str.intValue()! str[{0}]", str);
        }
        return ret;
    }

    public static float floatValue(this string str, float defaultValue = 0f) {
        float ret = defaultValue;
        if(false == float.TryParse(str, out ret)) {
            DebugUtil.LogErrorFormat("[intValue]Error! str.floatValue. str[{0}]", str);
        }
        return ret;
    }
}